#include "my_socket_server.h"

#include <arpa/inet.h>
#include <cstdio>
#include <mutex>
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>

#include "service_registry.h"

const sptr<MySocketServer> &MySocketServer::GetInstance()
{
    if (instance == nullptr) {
        static std::mutex mutex;
        std::lock_guard<std::mutex> lock(mutex);
        if (instance == nullptr) {
            instance = new MySocketServer();
        }
    }
    return instance;
}

int MySocketServer::Start(int port)
{
    socketFd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketFd < 0) {
        return -1;
    }

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(port),
        .sin_addr = {
            .s_addr = htonl(INADDR_ANY),
        },
    };
    auto ret = bind(socketFd, (struct sockaddr *)&addr, sizeof(struct sockaddr));
    if (ret < 0) {
        return ret;
    }
    printf("bind\n");

    constexpr int requestQueueSize = 20;
    ret = listen(socketFd, requestQueueSize);
    if (ret < 0) {
        return ret;
    }
    printf("listen\n");
    return ret;
}

void MySocketServer::Run()
{
    struct Message {
        int id;
        char buffer[1024];
    } message;

    interruptFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
    while (true) {
        // accept
        {
            struct sockaddr_in clientAddr;
            socklen_t addrlen = sizeof(sockaddr);
            struct pollfd pfd1[] = {
                { .fd = socketFd,    .events = POLLIN, },
                { .fd = interruptFd, .events = POLLIN, },
            };
            if (poll(pfd1, sizeof(pfd1) / sizeof(*pfd1), -1) < 0) {
                printf("poll failed\n");
                break;
            }

            if (pfd1[1].revents & POLLIN) {
                printf("interrupt\n");
                break;
            }

            if (pfd1[0].revents & POLLIN) {
                connFd = accept(socketFd, (struct sockaddr *)&clientAddr, &addrlen);
                printf("accept\n");
            }
        }

        // read, write
        {
            struct pollfd pfd2[] = {
                { .fd = connFd,      .events = POLLIN, },
                { .fd = interruptFd, .events = POLLIN, },
            };
            if (poll(pfd2, sizeof(pfd2) / sizeof(*pfd2), -1) < 0) {
                printf("poll failed\n");
                break;
            }

            if (pfd2[1].revents & POLLIN) {
                printf("interrupt\n");
                break;
            }

            if (pfd2[0].revents & POLLIN) {
                auto size = read(connFd, &message, sizeof(message));
                printf("read %lu\n", size);

                if (size > 0) {
                    auto serviceRegistry = delegator.Dep<ServiceRegistry>();
                    auto replyMessage = serviceRegistry->DispatchMessage(message.id, message.buffer);
                    write(connFd, replyMessage.c_str(), replyMessage.length());
                }
            }
        }

        close(connFd);
    }

    if (socketFd >= 0) {
        close(socketFd);
        socketFd = -1;
    }
    if (interruptFd >= 0) {
        close(interruptFd);
        interruptFd = -1;
    }
}

void MySocketServer::Stop()
{
    if (interruptFd < 0) {
        return;
    }
    uint64_t buf = 1;
    int32_t ret = 0;
    do {
        ret = write(interruptFd, &buf, sizeof(buf));
    } while (ret == -1 && errno == EINTR);
}
